package org.voidness.oje2d;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.util.Vector;

import org.voidness.oje2d.interfaces.GLImageInterface;

/** An animated image class. */
public class GLAnimatedImage implements GLImageInterface {
    /** The animated image's frames */
    private Vector<GLImage> frames = null;

    /** The ammount of time between frames */
    private int updateInterval;

    /** The current frame */
    private int currentFrame;

    /** Is this animated image repeating the loop forever? */
    private boolean isLoop;

    /** Time of the last frame change */
    private long lastTime;

    /** The current time */
    private long currentTime;

    /**
     * Is this animated image running (i.e. changing frames every
     * "updateInterval" ms?
     */
    private boolean running;

    /** The animated image's filename */
    private String filename = null;

    /**
     * Simple constructor.
     */
    public GLAnimatedImage() {
        init();
    }

    /**
     * The animated image's default constructor. Builds an animated image from a
     * strip of frames (i.e. single image with multiple images aligned side by
     * side.
     * 
     * @param mFilename The filename of the image to use
     * @param mOriginalWidth The width of the sub-images
     * @param mOriginalHeight The height of the sub-images
     * @param mWidth The target width of the animated image
     * @param mHeight The target height of the animated image
     * @param mNumberOfImages How many sub-images are there?
     */
    public GLAnimatedImage(String mFilename, int mOriginalWidth,
            int mOriginalHeight, int mWidth, int mHeight, int mNumberOfImages) {
        init();

        filename = mFilename;

        // Open the image
        Image image = (new javax.swing.ImageIcon(mFilename)).getImage();

        if (image == null || image.getWidth(null) < 0) {
            Utils.dump("Error reading image: " + mFilename, Utils.ERROR);
            System.exit(1);
        }

        // Extract The Images from the strip
        BufferedImage tex =
                new BufferedImage(image.getWidth(null), image.getHeight(null),
                        BufferedImage.TYPE_4BYTE_ABGR);
        Graphics texGraphics = tex.getGraphics();
        texGraphics.drawImage(image, 0, 0, null);
        // int xPos = 0;
        for (int i = 0; i < mNumberOfImages; i++) {
            // Add frame
            BufferedImage newImage =
                    tex.getSubimage(i * mOriginalWidth, 0, mOriginalWidth,
                            mOriginalHeight);
            frames.add(new GLImage(newImage, mWidth, mHeight, false));
        }
    }

    /**
     * Private initialization method. To be called by every constructor.
     */
    private void init() {
        frames = new Vector<GLImage>();
        updateInterval = 100; // default
        currentFrame = 0;
        isLoop = true; // Loop by default
        running = true;

        lastTime = System.currentTimeMillis();
        currentTime = lastTime;
    }

    /**
     * Returns the image's filename
     * 
     * @return The relative path of the image
     */
    public String getFilename() {
        return filename;
    }

    /**
     * Adds a frame to the animated image.
     * 
     * @param mNewFrame The new image to add
     */
    public void addFrame(GLImage mNewFrame) {
        frames.add(mNewFrame);
    }

    /**
     * Removes the frame at the given index.
     * 
     * @param mIndex The index of the frame to remove
     */
    public void removeFrameAt(int mIndex) {
        if (mIndex > 0 && mIndex < frames.size())
            frames.removeElementAt(mIndex);
    }

    /**
     * (Re)Defines the update interval, i.e. the time between frames.
     * 
     * @param mMilliseconds The new value for the update interval
     */
    public void setUpdateInterval(int mMilliseconds) {
        updateInterval = mMilliseconds;
    }

    /**
     * Set's the animation at a defined frame.
     * 
     * @param mIndex The index of the frame to select
     */
    public void setCurrentFrame(int mIndex) {
        currentFrame = mIndex;
    }

    /**
     * Resets the animation.
     */
    public void reset() {
        currentFrame = 0;
        running = true;
    }

    /**
     * Updates the animated image every "updateInterval" ms.
     */
    private void update() {
        currentTime = System.currentTimeMillis();
        if (currentTime - lastTime > updateInterval && running) {
            currentFrame++;
            lastTime = currentTime;

            if (currentFrame >= frames.size()) {
                currentFrame = 0;
                if (!isLoop)
                    running = false;
            }
        }
    }

    /**
     * Draws the animated image in the specified position, using full alpha
     * value.
     * 
     * @param mX The X coordinate where to draw
     * @param mY The Y coordinate where to draw
     */
    public void draw(int mX, int mY) {
        draw(mX, mY, 1.0f);
    }

    /**
     * Draws the animated image's current frame at the specified location with a
     * specified alpha value.
     * 
     * @param mX The X coordinate where to draw
     * @param mY The Y coordinate where to draw
     * @param mAlpha The alpha value to use
     */
    public void draw(int mX, int mY, float mAlpha) {
        if (!frames.isEmpty()) {
            // Over or underflow protection
            if ((currentFrame < 0 || currentFrame >= frames.size()) && running)
                currentFrame = 0;

            ((GLImage) frames.elementAt(currentFrame)).draw(mX, mY, mAlpha);

            update(); // Self update. Outside is just messier...
        }
    }

    /**
     * Removes all frames from the animation.
     */
    public void removeAllFrames() {
        frames.removeAllElements();
        reset();
    }

    /**
     * Sets whether or not the animation will loop forever.
     * 
     * @param mLoop True if we want the animation never to stop, false otherwise
     */
    public void setAnimationLoop(boolean mLoop) {
        isLoop = mLoop;
    }

    /**
     * Gets whether or not the animation is looping forever.
     * 
     * @return True if it is, false if it's not
     */
    public boolean hasAnimationLoop() {
        return isLoop;
    }

    /**
     * Returns the number of frames in the animated image
     * 
     * @return The number of frames
     */
    public int getNumberOfFrames() {
        return frames.size();
    }

    /**
     * Returns the current frame's index
     * 
     * @return The current index
     */
    public int getCurrentFrame() {
        return currentFrame;
    }
}